#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using UTable.Input;
using UTable.Input.MultiTouch;
using UTable.Objects;
using UTable.Objects.Controls;
using UTable.Objects.Handlers.Gestures;
using System.Windows.Media;
using System.Windows.Threading;

namespace UTable.Tests
{
    /// <summary>
    /// Interaction logic for LocalGestureEngineTest.xaml
    /// </summary>
    public partial class LocalGestureEngineTest : UObject
    {
        ScaleTransform Scale;
        RotateTransform Rotate;
        TranslateTransform Translate;
        SkewTransform Skew;

        public LocalGestureEngineTest()
        {
            InitializeComponent();
            LocalGestureEngine engine = new LocalGestureEngine(WriteBoard, this);

            OneContactGestureRecognizer recognizer1 = new OneContactGestureRecognizer();
            recognizer1.EnabledGestures.Add(OneContactGestureType.Tap);
            recognizer1.EnabledGestures.Add(OneContactGestureType.Translate);
            recognizer1.EnabledGestures.Add(OneContactGestureType.Hold);

            TwoContactsGestureRecognizer recognizer2 = new TwoContactsGestureRecognizer();
            recognizer2.EnabledGestures.Add(TwoContactsGestureType.Zoom);
            recognizer2.EnabledGestures.Add(TwoContactsGestureType.Rotate);            


            ThreeContactsGestureRecognizer recognizer3 = new ThreeContactsGestureRecognizer();
            recognizer3.EnabledGestures.Add(ThreeContactsGestureType.AnchorMove);

            OneContactCustomizedGestureRecognizer recognizer4 = new OneContactCustomizedGestureRecognizer();
            recognizer4.EnabledGestures.Add(OneContactCustomizedGestureType.Circle);

            engine.RegisterGesture(recognizer1);
            engine.RegisterGesture(recognizer2);
            engine.RegisterGesture(recognizer3);
            engine.RegisterGesture(recognizer4);

            engine.GestureDetected += new GestureDetectedEventHandler(OnGestureDetected);
            WriteBoard.MultiTouchProcessor.Handlers.Add(engine);

            TransformGroup group = new TransformGroup();
            Scale = new ScaleTransform();
            Rotate = new RotateTransform();
            Translate = new TranslateTransform();
            Skew = new SkewTransform();

            group.Children.Add(Skew);
            group.Children.Add(Rotate);
            group.Children.Add(Scale);
            group.Children.Add(Translate);
            Photo.RenderTransform = group;

            ShowMarkTimer = new DispatcherTimer();
            ShowMarkTimer.Interval = TimeSpan.FromSeconds(1);
            ShowMarkTimer.Tick += new EventHandler(ShowMarkTimer_Tick);
        }

        public void WriteLine(String text)
        {
            return;
            //Write(text + "\n");
        }

        public void Write(String text)
        {
            if (this.Console.Text.Length > 5000)
                this.Console.Text = this.Console.Text.Substring(this.Console.Text.Length / 2, this.Console.Text.Length / 2);

            this.Console.Text += text;
            this.ConsoleScroller.ScrollToEnd();
        }

        private void UButton_Pressed(object sender, ButtonEventArgs args)
        {
            this.Console.Text = "";
        }

        #region Gesture Handle

        DispatcherTimer ShowMarkTimer = null;

        void ShowCenterMark(Brush color)
        {
            SetCenterMarkColor(color);
            CenerMark.Visibility = Visibility.Visible;
        }

        void HideCenterMark()
        {
            CenerMark.Visibility = Visibility.Hidden;
        }

        void MoveCenterMark(Point position)
        {
            Canvas.SetLeft(CenerMark, position.X);
            Canvas.SetTop(CenerMark, position.Y);
        }

        void ShowCenterMarkForAMoment(Point position, Brush color)
        {
            if (ShowMarkTimer.IsEnabled)
            {
                ShowMarkTimer.Stop();
                SetCenterMarkColor(color);
            }
            else
            {
                ShowCenterMark(color);
                MoveCenterMark(position);
            }
            ShowMarkTimer.Start();
        }

        void SetCenterMarkColor(Brush color)
        {
            CenerMark.Fill = color;
        }

        void ShowMarkTimer_Tick(object sender, EventArgs e)
        {
            HideCenterMark();
            ShowMarkTimer.Stop();
        }

        void MovePhoto(Vector vecotr)
        {

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="center">relative to the photo</param>
        /// <param name="angle">the same with the RotateTransform's angle</param>
        void RotatePhoto(Point center, double angle)
        {
            // firstly set the center of the Rotate and Scale as the center given without changing the global position
            ChangeRoateAndZoomCenter(center);

            // then rotate the photo
            Rotate.Angle += angle;
        }

        void ZoomPhoto(Point center, double zoomFactor)
        {
            // firstly set the center of the Rotate and Scale as the center given without changing the global position
            ChangeRoateAndZoomCenter(center);

            // then zoom the photo
            Scale.ScaleX *= zoomFactor;
            Scale.ScaleY *= zoomFactor;
        }

        void ChangeRoateAndZoomCenter(Point center)
        {
            if (Rotate.CenterX != center.X || Rotate.CenterY != center.Y)
            {
                Point op = RotateAndScalePoint(new Point(0, 0), new Point(Rotate.CenterX, Rotate.CenterY), Rotate.Angle, Scale.ScaleX);
                Point np = RotateAndScalePoint(new Point(0, 0), center, Rotate.Angle, Scale.ScaleX);
                Vector newTranslate = op + new Vector(Translate.X, Translate.Y) - np;
                Translate.X = newTranslate.X;
                Translate.Y = newTranslate.Y;
                Rotate.CenterX = center.X;
                Rotate.CenterY = center.Y;
                Scale.CenterX = center.X;
                Scale.CenterY = center.Y;
            }
        }

        /// <summary>
        /// Rotate and scale a point relative to the center
        /// </summary>
        Point RotateAndScalePoint(Point target, Point center, double angle, double zoom)
        {
            if (target == center)
                return target;

            Vector vector = target - center;
            double length = vector.Length * zoom;
            double a = CalcClockwiseAngle(vector, new Vector(1, 0)) - angle;
            a = (a + 360) % 360;
            double y = Math.Sin(a * Math.PI / 180);
            double x = Math.Sqrt(1 - y * y);
            if (a > 90 && a < 270)
                x *= -1;
            Vector v = new Vector(x * length, y * length);
            return center + v;
        }

        /// <summary>
        /// Calculate the angle to rotate v1 to v2 clockwisely
        /// the result is from 0 to 360
        /// </summary>
        double CalcClockwiseAngle(Vector v1, Vector v2)
        {
            v1.Normalize();
            v2.Normalize();
            double angle = Math.Asin(v2.X * v1.Y - v1.X * v2.Y) / Math.PI * 180;
            double innerProduct = v1.X * v2.X + v1.Y * v2.Y;
            if (innerProduct < 0)
            {
                if (angle > 0)
                    angle = 180 - angle;
                else
                    angle = -180 - angle;
            }
            if (angle < 0)
                angle += 360;
            return angle;
        }

        Point TranslatePointToPhoto(Point p)
        {
            p = p - new Vector(Translate.X, Translate.Y);
            return RotateAndScalePoint(p, new Point(Scale.CenterX, Scale.CenterY), -Rotate.Angle, 1 / Scale.ScaleX);
        }

        void OnGestureDetected(GestureRecognizer sender, GestureDetectedEventArgs args)
        {
            WriteLine(args.DetectedGesture.ToString());

            Gesture gesture = args.DetectedGesture;

            if (gesture is TapGesture)
            {
                HandleTap(gesture as TapGesture);
            }
            else if (gesture is HoldGesture)
            {
                HandleHold(gesture as HoldGesture);
            }
            else if (gesture is TranslateGesture)
            {
                HandleTranslate(gesture as TranslateGesture);
            }
            else if (gesture is ZoomGesture)
            {
                HandleZoom(gesture as ZoomGesture);
            }
            else if (gesture is RotateGesture)
            {
                HandleRotate(gesture as RotateGesture);
            }
            else if (gesture is AnchorMoveGesture)
            {
                HandleAnchorMove(gesture as AnchorMoveGesture);
            }
        }

        void HandleTap(TapGesture gesture)
        {
            ShowCenterMarkForAMoment(gesture.Position, Brushes.Red);
        }

        void HandleHold(HoldGesture gesture)
        {
            ShowCenterMarkForAMoment(gesture.Position, Brushes.SaddleBrown);
        }

        void HandleTranslate(TranslateGesture gesture)
        {
            if (gesture.Type == TranslateGesture.TranslateType.Start)
            {
                ShowCenterMark(Brushes.Blue);
                MoveCenterMark(gesture.Position);
            }
            else if (gesture.Type == TranslateGesture.TranslateType.Move)
            {
                if (InputEventArgs.GetTimestamp() != gesture.Info.Contacts[0].TimeTrace.StarTime)
                {
                    System.Console.WriteLine("Global Object Manager: " + (InputEventArgs.GetTimestamp() - gesture.Info.Contacts[0].TimeTrace.GlobalObjectManagerTime));
                    System.Console.WriteLine("Before Send UObject: " + (InputEventArgs.GetTimestamp() - gesture.Info.Contacts[0].TimeTrace.BeforeSendUObjectTime));
                    System.Console.WriteLine("After Send UObject: " + (InputEventArgs.GetTimestamp() - gesture.Info.Contacts[0].TimeTrace.AfterSendUObjectTime));
                    System.Console.WriteLine("UObject: " + (InputEventArgs.GetTimestamp() - gesture.Info.Contacts[0].TimeTrace.UObjectTime));
                    System.Console.WriteLine("Total Time: " + (InputEventArgs.GetTimestamp() - gesture.Info.Contacts[0].TimeTrace.StarTime));
                    System.Console.WriteLine();
                }

                MoveCenterMark(gesture.Position);
                Translate.X += gesture.Offset.X;
                Translate.Y += gesture.Offset.Y;
            }
            else if (gesture.Type == TranslateGesture.TranslateType.End)
            {
                HideCenterMark();
            }
        }

        void HandleZoom(ZoomGesture gesture)
        {
            ShowCenterMarkForAMoment(gesture.ZoomCenter, Brushes.Firebrick);
            ZoomPhoto(TranslatePointToPhoto(gesture.ZoomCenter), gesture.ZoomFactor);
        }

        void HandleRotate(RotateGesture gesture)
        {
            ShowCenterMarkForAMoment(gesture.RotateCenter, Brushes.Fuchsia);
            RotatePhoto(TranslatePointToPhoto(gesture.RotateCenter), gesture.RotateAngle);
        }

        void HandleAnchorMove(AnchorMoveGesture gesture)
        {
            ShowCenterMarkForAMoment(gesture.AnchorPosition, Brushes.Violet);
            Skew.AngleX += gesture.Displacement.X / 10;
            Skew.AngleY += gesture.Displacement.Y / 10;
        }

        #endregion
    }
}
